package query;

import index.HashIndex;
import global.Minibase;
import global.RID;
import global.SearchKey;
import heap.HeapFile;
import heap.HeapScan;
import parser.AST_Update;
import relop.Predicate;
import relop.Schema;
import relop.Tuple;

/**
 * Execution plan for updating tuples.
 */
class Update implements Plan {

	protected String tableName;
	protected Predicate[][] predicates;
	protected Schema schema;
	protected IndexDesc[] indexes;
	protected Object[] values;
	protected String[] fields;
	protected int[] fieldNums;
  /**
   * Optimizes the plan, given the parsed query.
   * 
   * @throws QueryException if invalid column names, values, or pedicates
   */
  public Update(AST_Update tree) throws QueryException {
	  	tableName = tree.getFileName();
	    // check is table exists
	    QueryCheck.tableExists(tableName);

	    indexes = Minibase.SystemCatalog.getIndexes(tableName);
	    // check if indexes exist
	    for (IndexDesc index : indexes) {
	      QueryCheck.indexExists(index.indexName);
	    }
	    
	    // check if schema exists
	    schema = Minibase.SystemCatalog.getSchema(tableName);
	    if (schema == null) throw new QueryException("Invalid table Name");
	    
	    predicates = tree.getPredicates();
		// check if the predicates exists
		QueryCheck.predicates(schema, predicates);

	    fields = tree.getColumns();
	    // check if fields match schema
	    fieldNums = QueryCheck.updateFields(schema, fields);
	    
	    values = tree.getValues();	    
	    // check if values match schema
	    QueryCheck.updateValues(schema, fieldNums, values);
	    
  } // public Update(AST_Update tree) throws QueryException

  /**
   * Executes the plan and prints applicable output.
   */
  public void execute() {
	  int numRows = 0;
	  
	  HeapFile heapFile = new HeapFile(tableName);
	  HeapScan heapScan = heapFile.openScan();
	  
	  while (heapScan.hasNext()) {
		  RID rid = new RID();
		  Tuple tuple = new Tuple(schema, heapScan.getNext(rid));
		  // update tuple if it matches the predicates
		  if (qualify(tuple)) {              
              for (int i = 0; i < fields.length; i++) {
            	  // update indexes for the entry
    			  for (IndexDesc index : indexes) {
    				  if (index.columnName.equalsIgnoreCase(fields[i])) {
	    				  HashIndex hIndex = new HashIndex(index.indexName);
	    				  Object val = tuple.getField(index.columnName);
	    			      SearchKey key = new SearchKey(val);
	    			      hIndex.deleteEntry(key, rid);
	    			      key = new SearchKey(values[i]);
	    			      hIndex.insertEntry(key, rid); 
    				  }
    			  }
    			  tuple.setField(fields[i], values[i]);
              }	
			  // update entry in file
			  heapFile.updateRecord(rid, tuple.getData());
			  numRows++;
		  }
	  }
	  
    // print the output message
    System.out.println(numRows + " rows affected.");

  } // public void execute()

  /**
   * Checks if a tuple qualifies the predicate selection
   * @param tuple
   * @return returns true if the tuple qualifies for the predicates
   */
  private boolean qualify(Tuple tuple) {
	boolean retVal = true;
	
	for (Predicate[] outer : predicates) {
		
		for (Predicate inner : outer) {
			retVal = retVal | inner.evaluate(tuple);
			if (retVal == true) {
				break;
			}
		}
		
		if (retVal == false)
			break;
	}
	
	return retVal;
  }
  
} // class Update implements Plan
